看雪.京东 2018 CTF 第五题点评与解析
刺眼的阳光,公路上的热浪,聒噪的蝉鸣...
夏天就这样悄然走进
夏天有几件必须做的事情:
1、吃西瓜,冰镇的最佳
2、吹着风扇
然后参加看雪.京东 2018 CTF,哈哈哈
目前比赛已经进行到了第六题,来看看过去的第五题结束后,
第五题出题者以被6人攻破的成绩,位列第一位
本题过后 攻击方排名变化较大
qwertyaa 、风间仁各上升一名,分别上升至上升至第一、二位
NearJMP,则上升4位,直接从第7位上升至第三位。
比赛进程已过去三分之一,很明显,很多选手已经进入了状态。
比赛火药味渐浓
究竟谁能笑傲江湖?让我们拭目以待!
看雪版主&评委 netwind 点评
本题名为ExecuteTable,意为可执行表,是将密码表插入到函数中,并通过伪造的初始化函数,看着像是执行的插入密码表的函数。题目中加入了一些垃圾代码,加大了静态分析难度,并采用了反调试手段,隐藏了字符串和API函数名,并对关键函数进行了加密处理。要解此题一定要分析完反调试的逻辑,查看它在获取成功后做的操作,设法找到注册对应的真实的函数,待真实函数被解密时方可分析加密算法,理清流程后写出解密代码最终才可求解。此题难度较大,需要对APK逆向较熟悉,然后抽丝剥茧方可见真解!
看雪.京东 2018 CTF 第五题 作者简介
大帅锅
bbs.pediy.com/user-735473
第五题出题者简介:
Rorschach,本科嵌入式相关专业,立志从事软件开发,误入安全领域,所幸潜水看雪论坛,习得皮毛本领。第一次尝试开发CrackMe,脑洞不够大,还有很多不足之处,希望以后还能有这样的机会,搞点好玩的东西。
看雪.京东 2018 CTF 第五题 设计思路
0x00 题目类型
Android的CrackMe(ExecuteTable.apk,签名: 38e1ec0e)
将serial加密成一串字符串,与已保存字符串比较得到结果,需要分析加密算法,然后根据加密算法写出解密算法;题目中采用的加解密算法不需要太多的分析便能理清流程,关键点在于理清楚加解密的流程,涉及的相关功能点:
(1)GOT表Hook了JNI_OnLoad,并且对原有JNI_OnLoad前几十字节随便处理了一下;
(2)加入了一些垃圾代码;
(3)对用到的字符串都做了加密处理,肉眼可识别的字符串都是假的;
(4)读取tracer pid进行反调试,并且在反调试的地方设置了一个key值,只有pid为0时才是正确的key;
(5)一些系统函数使用系统调用实现,其他地方调用了系统函数(如open、read、mprotect等)也只是用于混淆,没有实际调用意义;
(6)关键的注册函数做了加密处理 ,静态直接分析不容易得出,需要动态运行dump内存等手段;
(1)执行.init_array中的函数,有些函数只是为了混淆,或者是为了设置某些全局变量,真正有分析意义的函数只有两个,一个启动了反调试线程,一个对JNI_OnLoad进行了GOT表Hook;
(2)hook的JNI_OnLoad函数,实现了对java层native函数注册的功能,注册完成后,对关键的注册函数进行了解密;
(3)调用注册函数时对序列号进行加密和已有的一个字符串(3ww3U53wOAWG333wwPZ56GGw0PO02OUW)进行比较,相同则成功。
(1)过反调试,反调试的办法并不复杂,只是对status文件中的TracerPid进行了检测是否为0,绕过很容易,但需注意,这里在判断为没有反调试时设置了密钥,该密钥最后会用来对序列号进行加密操作。
(2) hook一些自实现系统调用函数便于分析到关键函数,一些真正调用的函数通过系统调用实现(如read、open等)。
(3)捷径分析点:因为对关键函数进行了加密,动态解密进行调用,没有在调用后加密回去,利用这一点直接dump内存,和已有so比较,可以找到注册函数,同理可以找到GOT表Hook修改的函数:
C0ngRa7U1AtIoN2U
附件上传了用到的一些字符串加解密的python实现
一些功能实现点可以参考个人博客:https://blog.csdn.net/u011247544
你找到了ExecuteTable吗?
看雪.京东 2018 CTF 第五题解析
*本解析来自看雪论坛oooAooo
一、初步分析APK
1、解压APK后发现2个文件与代码相关:classes.dex和libexecute_table.so。
2、使用JEB打开apk 如下:
AAA46ESAB.kadoaskdopaskdadpakdpaosk 用于校验输入key是否合法,lkdakjudajndn函数是native函数,位于so文件中,用于key校验,返回1表示校验成功,其它校验失败。
3、使用010查看so文件是否被更改,如下图:
可以看出section段被修改了。直接修改header,让section偏移为0, section 段个数为0 ,使IDA利用segment段进行分析。修改后IDA可以分析出来对系统函数调用了。见下图:
准备工作做完开始进调试分析。
二、初始化函数分析
android对so调用的顺序是 init函数---> jni_onload。(对于如何断在初始化函数和jni_onload 可以看这个帖子IDA调试 android so文件的10个技巧)
so文件使用字符串对象操作进行代码的混淆,因此需要分析出相关字符串函数的含义,分析字符串对象处理函数是解这题的关键。分析的方法可以看输入和输出进行猜测,基本上都可以猜出来。
1、init_proc函数(啥也没干,混淆)
_DWORD *init_proc()
{
int v0; // r4
_DWORD *result; // r0
unsigned int v2; // r7
int v3; // r2
int v4; // r3
const char *v5; // r1
int v6; // r2
int v7; // r3
int v8; // r2
int v9; // r3
int v10; // r2
int v11; // r3
int v12; // [sp+8h] [bp-F8h]
unsigned __int8 *v13; // [sp+Ch] [bp-F4h]
int v14; // [sp+10h] [bp-F0h]
char v15; // [sp+14h] [bp-ECh]
char v16; // [sp+18h] [bp-E8h]
char v17; // [sp+1Ch] [bp-E4h]
char v18; // [sp+20h] [bp-E0h]
int string_ASBQ838ZquyW; // [sp+24h] [bp-DCh]
int v20; // [sp+2Ch] [bp-D4h]
int v21; // [sp+30h] [bp-D0h]
newString1(&string_ASBQ838ZquyW, (int)"ASBQ838ZquyW");
v0 = sub_8A68(&string_ASBQ838ZquyW);
result = deleteString(&string_ASBQ838ZquyW);
if ( v0 ) // v0=0
{
newString1(&v12, (int)"333");
newString1(&v13, (int)"%*s%d");
newString1(&v14, (int)"UYetrq736UMayFindMe233");
CreateUnknowStructFunc((int)&string_ASBQ838ZquyW, 24);
sub_F0A4((int)&v20, &v13);
sub_DD44(&v15, &v21);
sub_191A8(&v13);
v2 = *v13;
if ( std::operator==<char>(&v14, &v15) )
{
newString1(&v18, (int)"DABD786ABH");
if ( v2 == 1 )
{
v5 = "8a7d9Vduya";
}
else if ( v2 >= 1 )
{
if ( v2 == 2 )
v5 = "73812huvVQ";
else
v5 = "daj87YBDASYBvy";
}
else
{
v5 = "UDHA47DBsd";
}
SetStringNull((int)&v13, (int)v5, v3, v4);
copyString((int)&v13, (int)&v18);
deleteString(&v18);
}
if ( std::operator!=<char,std::char_traits<char>,std::allocator<char>>(&v14, &v15) )
{
SetStringNull((int)&v13, (int)&unk_30735, v6, v7);
newString1(&v16, (int)"DU8NABvA");
copyStringFromLocation((int)&v18, &v16, 0, 1u);
copyString((int)&v13, (int)&v18);
deleteString(&v18);
if ( std::operator!=<char,std::char_traits<char>,std::allocator<char>>(&v13, &unk_30735) )
SetStringNull((int)&v13, (int)&unk_30735, v8, v9);
copyStringFromLocation((int)&v17, &v16, 1u, 2u);
copyString((int)&v13, (int)&v17);
deleteString(&v17);
if ( std::operator!=<char,std::char_traits<char>,std::allocator<char>>(&v13, &unk_30735) )
SetStringNull((int)&v13, (int)&unk_30735, v10, v11);
deleteString(&v16);
}
GetStringBuf(&v16, (int *)&v13);
deleteString(&v15);
sub_DBAC(&string_ASBQ838ZquyW);
deleteString(&v14);
StringChange((int)&v12, (int)&v16);
deleteString(&v16);
deleteString(&v13);
if ( std::operator==<char,std::char_traits<char>,std::allocator<char>>(&v12, "333") )
sleep_0(0);
result = deleteString(&v12);
}
return result;
}
上面的有些函数可能参数个数不对,不过不影响分析。
2、初始化函数数组
LOAD:00034CD0 5D 62 00 00 DCD initfunction1+1
LOAD:00034CD4 5D 63 00 00 DCD initfunction2+1
LOAD:00034CD8 4D 65 00 00 DCD initfunction3+1 ; 啥也没做
LOAD:00034CDC BD 67 00 00 DCD sub_67BC+1 ; 创建反调试线程
LOAD:00034CE0 DD 6C 00 00 DCD sub_6CDC+1
LOAD:00034CE4 49 70 00 00 DCD sub_7048+1
LOAD:00034CE8 8D 73 00 00 DCD sub_738C+1
LOAD:00034CEC C5 76 00 00 DCD sub_76C4+1
LOAD:00034CF0 69 78 00 00 DCD sub_7868+1
LOAD:00034CF4 85 78 00 00 DCD sub_7884+1
LOAD:00034CF8 C5 78 00 00 DCD sub_78C4+1
LOAD:00034CFC D5 78 00 00 DCD sub_78D4+1
LOAD:00034D00 B1 79 00 00 DCD sub_79B0+1
LOAD:00034D04 8D 7A 00 00 DCD sub_7A8C+1
init_array函数先于jni_onload 函数运行,jni_onload应该被加密了,而上面的函数应该存在对其解密。
3、函数625D
该函数作用:36090 = lkdakjudajndn 函数地址,用于使用动态注册jni函数。其它的都是混淆。
4、函数635D
该函数主要作用: dword_36094 = 0x64。其它的都是混淆。
5、函数654D
该函数啥也没做。
6、函数67BC
该函数主要用于创建反调试线程B8BC,其它的没用。
_DWORD *sub_67BC()
{
char *v0; // r8
int *v1; // r8
unsigned int v2; // r10
int v3; // r2
int v4; // r3
const char *v5; // r1
int v6; // r2
int v7; // r3
int v8; // r2
int v9; // r3
int v10; // r2
int v11; // r3
char *v12; // r0
unsigned int v13; // r11
int v14; // r2
int v15; // r3
const char *v16; // r1
int v17; // r2
int v18; // r3
int v19; // r2
int v20; // r3
int v21; // r2
int v22; // r3
int malloc_20; // [sp+4h] [bp-1DCh]
char v25; // [sp+Ch] [bp-1D4h]
int string_daka97YGBB; // [sp+10h] [bp-1D0h]
char v27; // [sp+14h] [bp-1CCh]
int v28; // [sp+18h] [bp-1C8h]
char v29; // [sp+1Ch] [bp-1C4h]
int string_UYetrq736UMayFindMe233; // [sp+20h] [bp-1C0h]
char v31; // [sp+24h] [bp-1BCh]
char v32; // [sp+28h] [bp-1B8h]
char v33; // [sp+2Ch] [bp-1B4h]
int v34; // [sp+30h] [bp-1B0h]
int string_A782E192B81NICAIsan38Qz; // [sp+34h] [bp-1ACh]
char string_52651; // [sp+38h] [bp-1A8h]
char string_33687; // [sp+3Ch] [bp-1A4h]
char v38; // [sp+40h] [bp-1A0h]
char v39; // [sp+48h] [bp-198h]
char v40; // [sp+4Ch] [bp-194h]
char v41; // [sp+FCh] [bp-E4h]
int v42; // [sp+104h] [bp-DCh]
int v43; // [sp+108h] [bp-D8h]
malloc_20 = operator new(0x20u);
sub_C8A0(malloc_20);
newString1(&string_A782E192B81NICAIsan38Qz, (int)"A782E192B81NICAIsan38Qz");
CreateUnknowStructFunc((int)&v38, 24);
hextoString((int)&v39, 52651);
sub_DD44(&string_52651, &v40);
v0 = (_BYTE *)(&loc_CD76 + 2); // V0=CD78
if ( !std::operator==<char>(&string_A782E192B81NICAIsan38Qz, &string_52651) )
v0 = (_BYTE *)(&loc_CDAA + 1); // V0=CDAB
if ( std::operator!=<char,std::char_traits<char>,std::allocator<char>>(&string_52651, &string_A782E192B81NICAIsan38Qz) )
{
CreateUnknowStructFunc((int)&v41, 24);
hextoString((int)&v42, 33687);
sub_DD44(&string_33687, &v43);
v0 = (char *)std::operator==<char,std::char_traits<char>,std::allocator<char>>(&string_33687, "0d87a");
deleteString(&string_33687);
sub_DBAC(&v41);
if ( v0 )
v0 = (char *)off_35CFC + 0x4A04F;
}
deleteString(&string_52651);
sub_DBAC(&v38);
deleteString(&string_A782E192B81NICAIsan38Qz);
newString1(&v25, (int)"94DASIH78bdgskl998");
if ( !v0 )
{ // pthread_create
(*(void (__fastcall **)(char *, _DWORD, void (*)(), _DWORD))(malloc_20 + 28))(&string_33687, 0, anti_debug, 0);
v1 = &string_daka97YGBB;
newString1(&string_daka97YGBB, (int)"daka97YGBB");
newString1(&string_UYetrq736UMayFindMe233, (int)"UYetrq736UMayFindMe233");
CreateUnknowStructFunc((int)&v38, 24);
sub_F0A4((int)&v39, &string_daka97YGBB);
sub_DD44(&v31, &v40);
sub_191A8(&string_daka97YGBB);
v2 = *(unsigned __int8 *)string_daka97YGBB;
if ( std::operator==<char>(&string_UYetrq736UMayFindMe233, &v31) )
{
newString1(&v41, (int)"DABD786ABH");
if ( v2 == 1 )
{
v5 = "8a7d9Vduya";
}
else if ( v2 >= 1 )
{
if ( v2 == 2 )
v5 = "73812huvVQ";
else
v5 = "daj87YBDASYBvy";
}
else
{
v5 = "UDHA47DBsd";
}
SetStringNull((int)&string_daka97YGBB, (int)v5, v3, v4);
copyString((int)&string_daka97YGBB, (int)&v41);
deleteString(&v41);
}
if ( std::operator!=<char,std::char_traits<char>,std::allocator<char>>(&string_UYetrq736UMayFindMe233, &v31) )
{
SetStringNull((int)&string_daka97YGBB, (int)&unk_30735, v6, v7);
newString1(&v41, (int)"DU8NABvA");
copyStringFromLocation((int)&v33, &v41, 0, 1u);
copyString((int)&string_daka97YGBB, (int)&v33);
deleteString(&v33);
if ( std::operator!=<char,std::char_traits<char>,std::allocator<char>>(&string_daka97YGBB, &unk_30735) )
SetStringNull((int)&string_daka97YGBB, (int)&unk_30735, v8, v9);
copyStringFromLocation((int)&v32, &v41, 1u, 2u);
copyString((int)&string_daka97YGBB, (int)&v32);
deleteString(&v32);
if ( std::operator!=<char,std::char_traits<char>,std::allocator<char>>(&string_daka97YGBB, &unk_30735) )
SetStringNull((int)&string_daka97YGBB, (int)&unk_30735, v10, v11);
deleteString(&v41);
}
.....完整代码请点击阅读原文
后面的函数懒得分析,应该是代码解密的。先不管,看下反调线程。
三、反调试线程 B8BC
这个函数也加入了很多字符串垃圾指令。其主要做如下几件事:
1、解密字符串“tracePid”。
2、解密字符串“%*s%d”。
3、解密字符串“/proc/self/status”。
4、打开文件 “/proc/self/status”,一行一行读取,一直找到 “tracePid”为止。
5、调用scanf获取tracePid的数值。
6、如果tracePid > 0,则全局变量0x36098 = 0xBD9813BA,否则 0x36098 = 0x2333AE83。
7 、线程sleep 5秒。
0x36098 的值在对输入key进行变换时有用到,当程序处于调试状态时,会使key计算出错。过掉反调试方法很多,但是无论用啥方法必须要使0x36098 赋值为0x2333AE83 。可以让线程进入死循环,或者让线程终止,或者让线程sleep(100000)秒,也可以直接修改逻辑让tracePid恒等于0。
void anti_debug()
{
unsigned int v0; // r9
int v1; // r2
int v2; // r3
const char *v3; // r1
int v4; // r2
int v5; // r3
int v6; // r2
int v7; // r3
int v8; // r2
int v9; // r3
signed int v10; // r10
unsigned int i; // r7
int v12; // r11
unsigned int j; // r6
signed int v14; // r9
unsigned int k; // r7
int v16; // r10
unsigned int v17; // r5
signed int v18; // r9
unsigned int l; // r9
int v20; // r11
unsigned int v21; // r5
unsigned int v22; // r8
int v23; // r2
int v24; // r3
const char *v25; // r1
int v26; // r2
int v27; // r3
int v28; // r2
int v29; // r3
int v30; // r2
int v31; // r3
int v32; // r3
char *readBuf_1; // r6
int v34; // r3
char *v35; // r7
int string_format_3; // r0
int v37; // r3
int v38; // r2
char *v39; // r10
int fd; // [sp+0h] [bp-1080h]
char v41; // [sp+8h] [bp-1078h]
char v42; // [sp+8h] [bp-1078h]
unsigned int v43; // [sp+18h] [bp-1068h]
int *v44; // [sp+18h] [bp-1068h]
int new_20; // [sp+1Ch] [bp-1064h]
char v46; // [sp+30h] [bp-1050h]
unsigned __int8 read_1ByteBuf; // [sp+58h] [bp-1028h]
int string_W302sWW6O6WWb0b6W; // [sp+5Ch] [bp-1024h]
char string_AG60As3wWPCAsA6A; // [sp+60h] [bp-1020h]
int string_NULL_U8; // [sp+64h] [bp-101Ch]
_BYTE *string_NULL; // [sp+68h] [bp-1018h]
int string_racePid; // [sp+6Ch] [bp-1014h]
int string_AG60As3wWPCAsA6A_1; // [sp+70h] [bp-1010h]
int sring_P5U6UP2UsCP20CUW2; // [sp+74h] [bp-100Ch]
int string_proc_self_status; // [sp+78h] [bp-1008h]
int buf_W302sWW6O6WWb0b6WP5U6UP2UsCP20CUW2; // [sp+7Ch] [bp-1004h]
int string_format_2; // [sp+80h] [bp-1000h]
int string_format_1; // [sp+84h] [bp-FFCh]
int string_NULL_3; // [sp+88h] [bp-FF8h]
char v60; // [sp+8Ch] [bp-FF4h]
int string_UYetrq736UMayFindMe233; // [sp+90h] [bp-FF0h]
char string_W302sWW6O6WWb0b6W_2; // [sp+94h] [bp-FECh]
char v63; // [sp+98h] [bp-FE8h]
char string_D; // [sp+9Ch] [bp-FE4h]
int string_BQ366EYdQs_1; // [sp+A0h] [bp-FE0h]
char string_22_1; // [sp+A4h] [bp-FDCh]
int string_A782E192B81NICAIsan38Qz; // [sp+A8h] [bp-FD8h]
char string_1321; // [sp+ACh] [bp-FD4h]
char string_BQ366EYdQs; // [sp+B0h] [bp-FD0h]
char string_AG60As3wWPCAsA6A_2; // [sp+B4h] [bp-FCCh]
int string_NULL_2; // [sp+B8h] [bp-FC8h]
int string_BQ366EYdQs_2; // [sp+BCh] [bp-FC4h]
char string_22_3; // [sp+C0h] [bp-FC0h]
int string_A782E192B81NICAIsan38Qz_1; // [sp+C4h] [bp-FBCh]
char string_1321_1; // [sp+C8h] [bp-FB8h]
char v76; // [sp+CCh] [bp-FB4h]
char string_W302sWW6O6WWb0b6WP5U6UP2UsCP20CUW2; // [sp+D0h] [bp-FB0h]
int string_NULL_1; // [sp+D4h] [bp-FACh]
int string_BQ366EYdQs_3; // [sp+D8h] [bp-FA8h]
char string_22_4; // [sp+DCh] [bp-FA4h]
char v81; // [sp+E0h] [bp-FA0h]
char v82; // [sp+E4h] [bp-F9Ch]
char v83; // [sp+E8h] [bp-F98h]
char string_PW0PwWZ60Z_1; // [sp+ECh] [bp-F94h]
int string_format; // [sp+F0h] [bp-F90h]
int string_UYetrq736UMayFindMe233_2; // [sp+F4h] [bp-F8Ch]
char string_UYetrq736UMayFindMe233_1_1; // [sp+F8h] [bp-F88h]
......完整代码请阅读原文
四、jni_onload函数
此函数不能直接下断点,否则可能会使代码解密失败,可以直接在libdvm中下断点。这个函数主要作用是动态注册native lkdakjudajndn函数 从这里可知 lkdakjudajndn函数地址为:AC98。这个函数是真正的key校验过程。
五、函数lkdakjudajndn AC98
该函数比较大,主要做了如下事情:
1、将输入key的jstring对象转换成 cstring对象 。
2、key长度必须大于等于10,小于等于20。
3、将输入key置换到最后一位,变成key_change1。
4、索引 索引0x7FC2 ,key_change2[i] = 0x7FC2[ key_change1[i]],得到 key_change2。
5、将key_change2按照字节进行低4位与高四位的置换生成 key_change3。
6、将 key_change3以4字节形式与 0x36098内容进行异或,当处于调试状态是其值为 0xBD9813BA,否则为 0x2333AE83。得到 key_change4,从这里可知key长度为0x10。
7、将key_change4相邻2字节交换,得到key_change5,即 key_change5[2i] = key_change4[2i+1] key_change5[2i + 1] = key_change4[2i]。
8、再次索引7FC2 ,key_change6[i] = 7FC2[ key_change5 [i]],得到key_change6。
9、将key_change6的第一字符置换到最后位置,得到key_change7。
10、将key_change7的所有字符的高4位对字符串“A3Cw6Gb0OZWPU52s”进行索引,组成key_change8_highString,将key_change7所有字符的低4位对字符串“A3Cw6Gb0OZWPU52s”进行索引,并进行倒序。组成key_change8_lowString,然后key_change8_highString与key_change8_lowString进行拼接生成key_change8。
11、生成字符串“3ww3U53wOAWG333wwPZ56GGw0PO02OUW”。
12、将key_change8 与“3ww3U53wOAWG333wwPZ56GGw0PO02OUW”比较,相等返回1,否则返回其他。
该函数也被加密了,下面是解密后的:
char *__fastcall lkdakjudajndn(struct _JNIEnv *a1)
{
char *inputKey; // r0
char *v2; // r4
unsigned int i; // r4
int v4; // r2
int v5; // r3
char v6; // r6
unsigned int j; // r4
int v8; // r2
int v9; // r3
char *v10; // r6
int v11; // r8
int v12; // r2
int v13; // r3
signed int v14; // r10
unsigned int v15; // r7
char v16; // r8
unsigned int keylen; // r2
int v18; // r2
int v19; // r3
int v20; // r2
int v21; // r3
unsigned int v22; // r7
unsigned int v23; // r8
int string_A3Cw6Gb0OZWPU52s_1; // ST10_4
int v25; // ST10_4
int lowkeyChar; // ST10_4
unsigned int string_3d8ahnb1_1; // r6
int v28; // r2
int v29; // r3
const char *v30; // r1
int v31; // r2
int v32; // r3
int v33; // r2
int v34; // r3
int v35; // r2
int v36; // r3
int *v37; // r0
int inputKeyString; // [sp+20h] [bp-6B8h]
int newInputKeyBuf; // [sp+24h] [bp-6B4h]
int inputKeyBuf; // [sp+28h] [bp-6B0h]
int buf_3ww3U53wOAWG333wwPZ56GGw0PO02OUW; // [sp+2Ch] [bp-6ACh]
_BYTE *string_3d8ahnb1_is_U8; // [sp+30h] [bp-6A8h]
char buf_U8; // [sp+34h] [bp-6A4h]
char string_3; // [sp+38h] [bp-6A0h]
char buf_3w; // [sp+3Ch] [bp-69Ch]
char buf_3ww; // [sp+40h] [bp-698h]
char string_aA782e192b81nic; // [sp+44h] [bp-694h]
char v49; // [sp+48h] [bp-690h]
......完整代码请阅读原文
六、计算Key
下面是vc的代码:
unsigned char g_indexKey[0x100] =
{
0x40, 0x50, 0x78, 0x7A, 0x29, 0x88, 0xF7, 0x06, 0x21, 0x09, 0xF3, 0x5C, 0x95, 0xAE, 0x66, 0x12,
0x8F, 0x85, 0xC8, 0x5A, 0xBF, 0x33, 0x3D, 0x86, 0x90, 0x8C, 0xED, 0xD5, 0x8B, 0xA4, 0xC5, 0xC7,
0xEA, 0xF6, 0x79, 0x1E, 0x3C, 0xBA, 0x97, 0x4E, 0x38, 0x60, 0x08, 0xDD, 0xFA, 0xB3, 0xDE, 0x77,
0x81, 0x41, 0x19, 0xF4, 0x52, 0x6B, 0xFF, 0xD8, 0x2A, 0xC2, 0xBC, 0xB9, 0xE7, 0x91, 0xE9, 0x54,
0x82, 0xAD, 0x7E, 0x11, 0x35, 0x93, 0xB0, 0xA1, 0x18, 0xC4, 0x53, 0x0A, 0x74, 0x2F, 0xE2, 0x17,
0x98, 0x0C, 0x70, 0x92, 0x47, 0x64, 0x16, 0xFE, 0x75, 0x83, 0x37, 0x8D, 0x07, 0x72, 0x25, 0x04,
0xB7, 0xC9, 0xCE, 0x0E, 0x9E, 0xEB, 0xCF, 0xB1, 0xDB, 0x71, 0x56, 0xAF, 0x39, 0xF0, 0xBB, 0xBD,
0x46, 0x32, 0xE6, 0x9F, 0x4F, 0x1B, 0x4D, 0x68, 0xF2, 0x4B, 0x2E, 0xCB, 0x20, 0xD2, 0x0B, 0xA5,
0xEE, 0xE1, 0xA9, 0x2B, 0x84, 0x14, 0x67, 0x63, 0x6F, 0x3E, 0x7F, 0xFD, 0xB6, 0xFC, 0x55, 0x7C,
0x5F, 0xF8, 0x4C, 0x65, 0x2C, 0x30, 0xEF, 0x48, 0xD7, 0x0D, 0x0F, 0x1A, 0x5E, 0xC0, 0x3A, 0x57,
0x6A, 0x31, 0x00, 0xF1, 0x59, 0x10, 0xB8, 0x9A, 0x43, 0x73, 0xA3, 0x6E, 0x26, 0x1D, 0x13, 0x15,
0x89, 0x5D, 0xDA, 0x61, 0xD1, 0x6C, 0xD3, 0xE0, 0xD9, 0x1F, 0xD4, 0x49, 0xEC, 0xE3, 0xD0, 0x34,
0x36, 0xC6, 0x24, 0xE4, 0xF5, 0xAA, 0x9B, 0xB2, 0x4A, 0xDF, 0xAC, 0x96, 0xDC, 0xE8, 0xA0, 0xF9,
0xC1, 0x9C, 0xCA, 0x9D, 0x27, 0xC3, 0xBE, 0x87, 0x28, 0xCC, 0x99, 0xE5, 0x45, 0x58, 0x94, 0x23,
0x22, 0xFB, 0x02, 0x01, 0x03, 0x8A, 0x7B, 0xB5, 0x1C, 0xA7, 0x44, 0xCD, 0xA2, 0x51, 0x8E, 0x3F,
0x42, 0xD6, 0x69, 0xAB, 0x62, 0x3B, 0x7D, 0xA6, 0x05, 0x2D, 0xA8, 0x80, 0x6D, 0xB4, 0x76, 0x5B,
};
bool GetKey(unsigned char *lastKey, unsigned int keyLen)
{
unsigned int i, j;
unsigned char temp;
unsigned char lowByte = 0;
unsigned char highByte = 0;
unsigned char* highKey = (unsigned char*)"3ww3U53wOAWG333w";
unsigned char* lowKey = (unsigned char*)"wPZ56GGw0PO02OUW";
unsigned char* constKey = (unsigned char*)"A3Cw6Gb0OZWPU52s";
memset(lastKey, 0, keyLen);
//索引constKey
for (i = 0; i < keyLen; i++)
{
for (j = 0; j < keyLen; j++)
{
if (highKey[i] == constKey[j])
break;
}
if (j >= keyLen)
return false;
highByte = j << 4;
for (j = 0; j < keyLen; j++)
{
if (lowKey[i] == constKey[j])
break;
}
if (j >= keyLen)
return false;
lowByte = j;
lastKey[i] |= highByte;
lastKey[keyLen - 1 - i] |= lowByte;
}
//最后一个字节置换到第一个位置
temp = lastKey[keyLen-1];
for (i = keyLen-1; i >0; i--)
{
lastKey[i] = lastKey[i - 1];
}
lastKey[i] = temp;
//索引g_indexKey
for (i = 0; i < keyLen; i++)
{
for (j = 0; j < 256; j++)
{
if (lastKey[i] == g_indexKey[j])
break;
}
if (j >= 256)
return false;
lastKey[i] = j;
}
//相邻字节交换
for (i = 0; i < (keyLen>>1); i++)
{
temp = lastKey[i * 2];
lastKey[i * 2] = lastKey[i * 2 + 1];
lastKey[i * 2 + 1] = temp;
}
//与0x2333AE83异或
unsigned int eorConstKey = 0x2333AE83;
unsigned int* p = (unsigned int*)lastKey;
for (i = 0; i < (keyLen >> 2); i++)
{
p[i] = p[i] ^ eorConstKey;
}
//高4位与低4位交换
for (i = 0; i < keyLen; i++)
{
highByte = lastKey[i] >> 4;
lowByte = lastKey[i] & 0xf;
lastKey[i] = (lowByte << 4) | highByte;
}
//索引g_indexKey
for (i = 0; i < keyLen; i++)
{
for (j = 0; j < 256; j++)
{
if (lastKey[i] == g_indexKey[j])
break;
}
if (j >= 256)
return false;
lastKey[i] = j;
}
//最后一个字节置换到第一个位置
temp = lastKey[keyLen - 1];
for (i = keyLen - 1; i >0; i--)
{
lastKey[i] = lastKey[i - 1];
}
lastKey[i] = temp;
return true;
}
sn = "C0ngRa7U1AtIoN2U"
CTF 寄语
netwind
bbs.pediy.com/user-39732
netwind:
2017年,看雪.Wifi万能钥匙 2017CTF年中赛和看雪.TSRC 2017CTF秋季赛连续成功举办,目前看雪CTF竞赛已经是国内逆向领域最专业、影响力最广的赛事。2018年,看雪CTF竞赛即将拉开序幕。本届大赛将依然严格按照竞赛规则进行,保证大赛的公平、公正、公开。
瑾代表看雪2018CTF大赛组委会并以大赛评委的名义对所有参加比赛的选手表示热烈的欢迎!相信所有参赛选手都会有不一样的收获,相信此次大赛会因你们精湛的技术而变得分外精彩!再此特别感谢所有关注、支持和帮助大赛的各界朋友,也希望大家持续关注和支持看雪CTF大赛!
我们希望能有更多的朋友来参与看雪CTF大赛,设计优质的作品,分享奇特的破解思路,结识朋友,探讨交流,共同步入技术的巅峰!
iweizime
bbs.pediy.com/user-677218
iweizime:
祝各位CTFer看汇编如同看小说,在看雪CTF中取得好成绩,更重要的是在比赛过程中能学到新的东西,提高自己的知识水平。
京东集团是中国收入最大的互联网企业之一,于2014年5月在美国纳斯达克证券交易所正式挂牌上市,业务涉及电商、金融和物流三大板块。
京东是一家技术驱动成长的公司,并发布了“第四次零售革命”下的京东技术发展战略。信息安全作为保障业务发展顺利进行的基石发挥着举足轻重的作用。为此,京东信息安全部从成立伊始就投入大量技术和资源,支撑京东全业务线安全发展,为用户、供应商和京东打造强大的安全防护盾。
随着京东全面走向技术化,大力发展人工智能、大数据、机器自动化等技术,将过去十余年积累的技术与运营优势全面升级。面向AI安全、IoT安全、云安全的机遇及挑战,京东安全积极布局全球化背景下的安全人才,开展前瞻性技术研究,成立了硅谷研发中心、安全攻防实验室等,并且与全球AI安全领域知名的高校、研究机构建立了深度合作。
京东不仅积极践行企业安全责任,同时希望以中立、开放、共赢的态度,与友商、行业、高校、政府等共同建设互联网安全生态,促进整个互联网的安全发展。
CTF 旗帜已经升起,等你来战!
扫描二维码,立即参战!
看雪.京东 2018 CTF
看雪2018安全开发者峰会
2018年7月21日,拥有18年悠久历史的老牌安全技术社区——看雪学院联手国内最大开发者社区CSDN,倾力打造一场技术干货的饕餮盛宴——2018 安全开发者峰会,将在国家会议中心隆重举行。会议面向开发者、安全人员及高端技术从业人员,是国内开发者与安全人才的年度盛事。此外峰会将展现当前最新、最前沿技术成果,汇聚年度最强实践案例,为中国软件开发者们呈献了一份年度技术实战解析全景图。
戳下图↓,立即购票,享5折优惠!